#include "http.h"
#include "socket.h"
#include <algorithm>

std::string ToString(int number)
{
  std::string buf;
  buf.reserve(35);
  int quotient = number;
  int base = 10;
  do 
  {
    buf += "0123456789abcdef"[std::abs(quotient % base)];
    quotient /= base;
  } while (quotient != 0);
  if (number < 0) buf += '-';
  std::reverse(buf.begin(), buf.end());
  return buf;
}

std::string GetRange(const std::string& file, int range1, int range2)
{
  FILE* fp = fopen(file.c_str(), "rb");
  fseek(fp, 0, SEEK_END);
  int len = ftell(fp);
  fclose(fp);
  std::string range;
  range += ToString(range1);
  range += "-";
  range += ToString(range2);
  range += "/";
  range += ToString(len);
  return range;
}

Http::Http(Response::Type response_)
: response(response_)
{
}

Http::Http(const std::string& request_)
: request(request_)
{
  std::transform(request.begin(), request.end(), request.begin(), ::tolower);
}

std::string Http::ResponseToString(Response::Type response)
{
  switch(response)
  {
  case Response::Ok: return std::string("OK");
  case Response::PartialContent: return std::string("PARTIAL CONTENT");
  case Response::Found: return std::string("FOUND");
  case Response::NotFound: return std::string("NOT FOUND");
  }
  return std::string();
}

std::string Http::GetRequestURL() const
{
  size_t start = request.find(' ') + 1;
  size_t end = request.find(' ', start);
  return request.substr(start, end - start);
}

std::string Http::GetRequestMethod() const
{
  size_t firstSpace = request.find(' ');
  return request.substr(0, firstSpace);
}

std::string Http::GetRequestHost() const
{
  size_t host = request.find("host: ") + 6;
  if (host == std::string::npos)
    return std::string();
  return request.substr(host, request.find('\r', host) - host);
}

void Http::AddHeader(const std::string& header, const std::string& value)
{
  headers.push_back(header + ": " + value);
}

void Http::SetContentHTML(const std::string& html_)
{
  htmlContent = html_;
}

void Http::SetContentBinary(const std::string& file_)
{
  file = file_;
  range1 = 0;
  range2 = 0;
}

void Http::SetContentBinary(const std::string& file_, int range1_, int range2_)
{
  AddHeader("Content-Type", GetMimeForFilename(file_));
  AddHeader("Content-Length", ToString(range2_ - range1_ + 1));
  AddHeader("Content-Range", "bytes " + GetRange(file_, range1_, range2_));
  file = file_;
  range1 = range1_;
  range2 = range2_;
}

static bool EndsWith(const std::string& fileName, const char* part, int len)
{
  if (fileName.length() > (size_t)len)
    return fileName.compare(fileName.length() - len, len, part) == 0;
  return false;
}

std::string Http::GetMimeForFilename(const std::string& fileName)
{
  if (EndsWith(fileName, ".jpg", sizeof(".jpg") - 1))
    return std::string("image/jpeg");
  if (EndsWith(fileName, ".tga", sizeof(".tga") - 1))
    return std::string("image/x-tga");
  if (EndsWith(fileName, ".mp4", sizeof(".mp4") - 1))
    return std::string("video/mp4");
  return std::string();
}

void Http::GetRequestRange(int* range1, int* range2) const
{
  size_t pos1 = request.find("range: bytes=");
  size_t pos2 = request.find("-", pos1);
  size_t pos3 = request.find("\r", pos2);
  std::string s1 = (pos1 == -1 || pos2 == -1 ? "" : request.substr(pos1 + 13, pos2 - pos1 - 13));
  std::string s2 = (pos2 == -1 || pos3 == -1  ? "" : request.substr(pos2 + 1, pos3 - pos2 - 1));
  int range1_ = atoi(s1.c_str());
  int range2_ = atoi(s2.c_str());
  if (range2_ == 0)
    range2_ = 1;
  if (range1 != 0)
    *range1 = range1_;
  if (range2 != 0)
    *range2 = range2_;
}

void Http::WriteResponseToSocket(Socket& socket)
{
  std::string s;
  s.reserve(1024);
  s += "HTTP/1.1 " + ToString((int)response) + " " + ResponseToString(response) + "\r\n";
  for (size_t i = 0; i < headers.size(); ++i)
    s += headers[i] + "\r\n";
  s += "\r\n";
  if (file.size() > 0)
  {
    socket.WriteString(s);
    socket.WriteBinary(file, range1, range2);
  }
  else if (htmlContent.size() > 0)
  {
    s += htmlContent;
    socket.WriteString(s);
  }
//  httpHeaders.Add("Content-Type: " + mime);
//  httpHeaders.Add("Content-Length: " + fileContent.Length.ToString());
  // GetMimeForFilename file
  //   sockets[i]->WriteString(s);
  //   sockets[i]->CloseConnection();
  // "\r\n"

//
//   int length = 0;//GetFileSize(file);
//   s.clear();
//   s += "Content-Type: video/mp4\r\n";
//   s += "Connection: keep-alive\r\n";
//   s += "Content-Range: bytes "; s += ToString(range1); s += "-"; s += ToString(range2); s += "/"; s += ToString(length); s += "\r\n";
//   s += "Content-Length: "; s += ToString(range2 - range1 + 1); s += "\r\n";
//   s += "Accept-Ranges: bytes\r\n";
//   s += "Cache-Control: no-cache\r\n";
//   s += "Expires: 0\r\n";
//   s += "\r\n";
}
